import React, { useCallback, useEffect, useRef, useState } from "react";
import { Splitter } from "antd";
import debounce from "lodash/debounce";
import type { Types } from "@cornerstonejs/core";
import {
  RenderingEngine,
  Enums,
  setVolumesForViewports,
  volumeLoader,
  getRenderingEngine,
} from "@cornerstonejs/core";
import * as cornerstoneTools from "@cornerstonejs/tools";
// @ts-expect-error js module
import createImageIdsAndCacheMetaData from "@/libs/createImageIdsAndCacheMetaData.js";
// @ts-expect-error js module
import setCtTransferFunctionForVolumeActor from "@/libs/setCtTransferFunctionForVolumeActor.js";
import addManipulationBindings from "@/libs/addManipulationBindings";
import { commandService } from "@/services";
import {
  MPR_VIEWPORT_ID as VIEWPORT_ID,
  COMMAND_NAMES,
} from "@/services/types";
import type { CmdOptions } from "@/services/types";
import type { MprDataProps } from "./types";
import Viewport from "./viewport";

const {
  ToolGroupManager,
  Enums: csToolsEnums,
  CrosshairsTool,
  PanTool,
  ZoomTool,
  StackScrollTool,
  WindowLevelTool,
  LengthTool,
  synchronizers,
} = cornerstoneTools;
const { createSlabThicknessSynchronizer } = synchronizers;
const { MouseBindings } = csToolsEnums;
const { ViewportType } = Enums;

// Define a unique id for the volume
const volumeName = "CT_VOLUME_ID"; // Id of the volume less loader prefix
const volumeLoaderScheme = "cornerstoneStreamingImageVolume"; // Loader id which defines which volume loader to use
const volumeId = `${volumeLoaderScheme}:${volumeName}`; // VolumeId with loader id + volume id
const toolGroupId = "MY_TOOLGROUP_ID";
const viewportIds = [
  VIEWPORT_ID.CT_AXIAL,
  VIEWPORT_ID.CT_SAGITTAL,
  VIEWPORT_ID.CT_CORONAL,
];
const renderingEngineId = "myRenderingEngine";
const synchronizerId = "SLAB_THICKNESS_SYNCHRONIZER_ID";

let synchronizer: cornerstoneTools.Synchronizer;
function setUpSynchronizers() {
  synchronizer = createSlabThicknessSynchronizer(synchronizerId);
  viewportIds.forEach((viewportId) => {
    synchronizer.add({
      renderingEngineId,
      viewportId,
    });
  });
  synchronizer.setEnabled(true);
}

let renderingEngine: RenderingEngine;

const MprPage: React.FC<MprDataProps> = ({ data }: MprDataProps) => {
  const axialRef = useRef<HTMLDivElement>(null);
  const sagittalRef = useRef<HTMLDivElement>(null);
  const cornalRef = useRef<HTMLDivElement>(null);

  const [imageIds, setImageIds] = useState<string[]>([]);
  const [viewports, setViewports] = useState<Types.PublicViewportInput[]>([]);
  const [volume, setVolume] = useState<Record<string, unknown>>();

  useEffect(() => {
    const fetchImageIds = async () => {
      const ids = await createImageIdsAndCacheMetaData({
        StudyInstanceUID: data.studyUid,
        SeriesInstanceUID: data.seriesUid,
        wadoRsRoot: data.wadoRoot,
      });
      setImageIds(ids);
    };
    fetchImageIds();
  }, [data]);

  useEffect(() => {
    if (imageIds.length == 0 || volume) return;
    const fetchVolume = async () => {
      const vol = await volumeLoader.createAndCacheVolume(volumeId, {
        imageIds,
      });
      setVolume(vol);
    };
    fetchVolume();
    // eslint-disable-next-line
  }, [imageIds]);

  useEffect(() => {
    if (
      viewports.length > 0 ||
      !axialRef.current ||
      !sagittalRef.current ||
      !cornalRef.current
    )
      return;

    const viewportInputArray: Types.PublicViewportInput[] = [
      {
        viewportId: VIEWPORT_ID.CT_AXIAL,
        type: ViewportType.ORTHOGRAPHIC,
        element: axialRef.current,
        defaultOptions: {
          orientation: Enums.OrientationAxis.AXIAL,
          background: [0, 0, 0],
        },
      },
      {
        viewportId: VIEWPORT_ID.CT_SAGITTAL,
        type: ViewportType.ORTHOGRAPHIC,
        element: sagittalRef.current,
        defaultOptions: {
          orientation: Enums.OrientationAxis.SAGITTAL,
          background: [0, 0, 0],
        },
      },
      {
        viewportId: VIEWPORT_ID.CT_CORONAL,
        type: ViewportType.ORTHOGRAPHIC,
        element: cornalRef.current,
        defaultOptions: {
          orientation: Enums.OrientationAxis.CORONAL,
          background: [0, 0, 0],
        },
      },
    ];
    setViewports(viewportInputArray);
  }, [viewports]);

  if (!renderingEngine) {
    renderingEngine = new RenderingEngine(renderingEngineId);
  }

  useEffect(() => {
    if (!volume || viewports.length == 0) return;

    renderingEngine.setViewports(viewports);
    setVolumesForViewports(
      renderingEngine,
      [
        {
          volumeId,
          callback: setCtTransferFunctionForVolumeActor,
        },
      ],
      viewportIds
    );
  }, [volume, viewports]);

  useEffect(() => {
    if (!volume) return;

    // @ts-expect-error volume load
    volume.load();

    cornerstoneTools.addTool(CrosshairsTool);
    const toolGroup = ToolGroupManager.createToolGroup(toolGroupId);
    if (toolGroup) {
      addManipulationBindings(toolGroup);

      toolGroup.addViewport(VIEWPORT_ID.CT_AXIAL, renderingEngineId);
      toolGroup.addViewport(VIEWPORT_ID.CT_SAGITTAL, renderingEngineId);
      toolGroup.addViewport(VIEWPORT_ID.CT_CORONAL, renderingEngineId);
    }

    setUpSynchronizers();
    // Render the image
    renderingEngine.renderViewports(viewportIds);
  }, [volume]);

  useEffect(() => {
    const setBlendMode = (mode: Enums.BlendModes) => {
      const toolGroup = ToolGroupManager.getToolGroup(toolGroupId);
      const crosshairsInstance = toolGroup?.getToolInstance(
        CrosshairsTool.toolName
      );
      const oldConfiguration = crosshairsInstance.configuration;

      crosshairsInstance.configuration = {
        ...oldConfiguration,
        slabThicknessBlendMode: mode,
      };

      // Update the blendMode for actors to instantly reflect the change
      toolGroup?.viewportsInfo.forEach(({ viewportId, renderingEngineId }) => {
        const renderingEngine = getRenderingEngine(renderingEngineId);
        const viewport = renderingEngine?.getViewport(
          viewportId
        ) as Types.IVolumeViewport;

        viewport.setBlendMode(mode);
        viewport.render();
      });
    };

    const sub = commandService.subscribe({
      next: (options: CmdOptions) => {
        const toolGroup = ToolGroupManager.getToolGroup(toolGroupId);
        const disableActvieTool = () => {
          const activeToolName = toolGroup?.getActivePrimaryMouseButtonTool();
          if (activeToolName) {
            const activeToolOptions = toolGroup?.getToolConfiguration(
              activeToolName
            ) as { disableOnPassive: boolean };

            if (activeToolOptions?.disableOnPassive) {
              toolGroup?.setToolDisabled(activeToolName);
            } else {
              toolGroup?.setToolPassive(activeToolName);
            }
          }
        };
        let viewport;

        switch (options.name) {
          case COMMAND_NAMES.EXIT:
            break;
          case COMMAND_NAMES.RESET:
            viewport = getRenderingEngine(renderingEngineId)?.getViewport(
              VIEWPORT_ID.CT_AXIAL
            ) as Types.IVolumeViewport;
            viewport.resetCamera(true, true, true, true, false);
            // @ts-expect-error reset wl
            viewport.resetToDefaultProperties(volumeId);
            viewport.render();
            break;
          case COMMAND_NAMES.PAN:
            disableActvieTool();
            toolGroup?.setToolActive(PanTool.toolName, {
              bindings: [{ mouseButton: MouseBindings.Primary }],
            });
            break;
          case COMMAND_NAMES.ZOOM:
            disableActvieTool();
            toolGroup?.setToolActive(ZoomTool.toolName, {
              bindings: [{ mouseButton: MouseBindings.Primary }],
            });
            break;
          case COMMAND_NAMES.CROSSHAIR:
            disableActvieTool();
            toolGroup?.setToolActive(CrosshairsTool.toolName, {
              bindings: [{ mouseButton: MouseBindings.Primary }],
            });
            break;
          case COMMAND_NAMES.WINDOW:
            disableActvieTool();
            toolGroup?.setToolActive(WindowLevelTool.toolName, {
              bindings: [{ mouseButton: MouseBindings.Primary }],
            });
            break;
          case COMMAND_NAMES.SCROLL:
            disableActvieTool();
            toolGroup?.setToolActive(StackScrollTool.toolName, {
              bindings: [{ mouseButton: MouseBindings.Primary }],
            });
            break;
          case COMMAND_NAMES.LENGTH:
            disableActvieTool();
            toolGroup?.setToolActive(LengthTool.toolName, {
              bindings: [{ mouseButton: MouseBindings.Primary }],
            });
            break;
          case COMMAND_NAMES.MAXIP:
            setBlendMode(Enums.BlendModes.MAXIMUM_INTENSITY_BLEND);
            break;
          case COMMAND_NAMES.AVGIP:
            setBlendMode(Enums.BlendModes.AVERAGE_INTENSITY_BLEND);
            break;
          case COMMAND_NAMES.MINIP:
            setBlendMode(Enums.BlendModes.MINIMUM_INTENSITY_BLEND);
            break;
          default:
            break;
        }
      },
    });
    return () => {
      sub.unsubscribe();
    };
  }, []);

  const onResize = useCallback(
    debounce(() => {
      getRenderingEngine(renderingEngineId)?.resize();
    }, 100),
    []
  );

  return (
    <Splitter>
      <Splitter.Panel className="!p-[0]" defaultSize={"60%"} collapsible={true}>
        <Viewport
          ref={axialRef}
          imageId={imageIds[0]}
          classes="h-full"
          onResize={onResize}
        />
      </Splitter.Panel>
      <Splitter.Panel>
        <Splitter layout="vertical">
          <Splitter.Panel
            className="!p-[0]"
            defaultSize={"51%"}
            collapsible={true}
          >
            <Viewport
              ref={sagittalRef}
              imageId={imageIds[0]}
              classes="h-[calc(100%-1px)]"
              onResize={onResize}
            ></Viewport>
          </Splitter.Panel>
          <Splitter.Panel className="!p-[0]" collapsible={true}>
            <Viewport
              ref={cornalRef}
              imageId={imageIds[0]}
              classes="h-full"
              onResize={onResize}
            ></Viewport>
          </Splitter.Panel>
        </Splitter>
      </Splitter.Panel>
    </Splitter>
  );
};

export default MprPage;
